#!/usr/bin/env perl

#
# Copyright (c) 2002-2004 Eric Wallengren
# This file is part of the Continuous Automated Build and Integration
# Environment (CABIE)
#
# CABIE is distributed under the terms of the GNU General Public
# License version 2 or any later version.  See the file COPYING for copying
# permission or http://www.gnu.org.
#
# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED OR
# IMPLIED, without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  ANY USE IS AT YOUR OWN RISK.
#
# Permission to modify the code and to distribute modified code is granted,
# provided the above notices are retained, and a notice that the code was
# modified is included with the above copyright notice.
#

BEGIN {
    push @INC, "lib";
    push @INC, "../lib";
    push @INC, "../../lib";
}

use Getopt::Long;
use File::Basename;
use English;
use Sys::Hostname;

my $hostname = hostname();
$hostname    =~ s/\.[a-zA-Z0-9\n]+//g;
$hostname    =~ s/-//g;

require "$hostname.pm";

my $config = new $hostname;

my $logo   = $config->LOGOICON;
my $prog   = basename( $PROGRAM_NAME );

use strict;

use vars qw(
    $opt_help
    $opt_infile
    $opt_outfile
    $opt_errorfile 
    $opt_refresh 
    $opt_binary 
    $opt_server
    $opt_port
    $opt_job );

# ------------------------------------------------------------------
my( @whenlist ) = ( "build", "package" );
my( $output ) = "";
my( $num_sections ) = 0;
my( $num_warnings ) = 0;
my( $num_errors ) = 0;
my( @error_list );
my( %section_link );
my( %warn_link );
my( %error_link );
my( $module_order );
my( %elapsed_time );
my( %products );
my( %ignored );
my( $image_platform );
my( $image_product );

# ------------------------------------------------------------------
# Load the list of error strings from a file.

sub loadErrorList
{
    my( $error_file ) = @_;

    open( ERRORFILE, "<$error_file" )
        or die "Can't open $error_file";

    @error_list = <ERRORFILE>;

    close( ERRORFILE );

    chomp @error_list;
}

# ------------------------------------------------------------------
# Return 1 if an error string is detected in the input line.

sub hasError
{
    my( $line ) = @_;
    my( $err );

    foreach $err ( @error_list )
    {
        if ( index( $line, $err ) > -1 )
        {
            return 1;
        }
    }

    return 0;
}

# ------------------------------------------------------------------
# Escape HTML characters.  "<" -> &lt; ">" -> &gt; "&" -> &amp;
sub esc
{
    my( $text ) = @_;

    $text =~ s/&/&amp;/g;
    $text =~ s/</&lt;/g;
    $text =~ s/>/&gt;/g;

    return $text;
}

# ------------------------------------------------------------------
sub processLog
{
    my( $in_header ) = 0;
    my( $when );
    my( %seen );
    my( %seen_product );
    my( $who );
    my( @contents );
    my( @tmpcontents );
    my( $line );

    while ( <BUILDLOG> )
    {
        if ( /^\s*(\[echo\])?\s*#-----------------/ )
        {
            if ( ! $in_header )
            {
                $output = $output .
                         "</pre>\n" .
                         "<h4 class='section'>\n" .
                         "<pre>\n" .
                         esc( $_ );

                $in_header = 1;
            }
            else
            {
                $in_header = 0;

                $output = $output .
                         esc( $_ ) .
                         "</pre>\n" .
                         "</h4>\n" .
                         "<pre>\n";
            }
        }
        elsif ( /^\s*\[create-distribution\]\s*creating.*image/ )
        {
            my( @words ) = split( " " );

            $image_platform = $words[ $#words - 2 ] eq "creating"
                            ? "all"
                            : $words[ $#words - 2 ];

            $image_product = $words[ $#words - 1 ];

            $num_sections++;

            $output = $output .
                     "</pre>\n" .
                     "<h4 class='section'>\n" .
                     "<a name='section_$num_sections'><pre>$_</pre></a>\n" .
                     "</h4>\n" .
                     "<pre>\n";

            $who = "$image_product-$image_platform";
            $section_link{ $who, "image" } = $num_sections;

            if ( ! $seen_product{ $who } )
            {
                $seen_product{ $who } = 1;
                $products{ $image_product } .= " " . $image_platform;
            }
        }
        elsif ( /^\s*\[create-distribution\]\s*elapsed staging time:/ )
        {
            my( @words ) = split( " " );
            $output = $output . esc( $_ );
            $elapsed_time{ $who, "image" } = $words[ $#words - 1 ];
        }
        elsif ( /^\s*\[create-distribution\]\s*elapsed iso time:/ )
        {
            my( @words ) = split( " " );
            $output = $output . esc( $_ );
            $elapsed_time{ $who, "iso" } = $words[ $#words - 1 ];
        }
        elsif ( $in_header eq 1 )
        {
            my( $ignore );
            my( @words ) = split( " " );

            chomp;

            $num_sections++;
            $output = $output . "<a name='section_$num_sections'>" . esc( $_ ) .
                                "</a>\n";

            $ignore = $words[ $#words - 2 ] eq "Ignore";

            if ( $words[ $#words ] eq "module" )
            {
                $when = "build";
            }
            elsif ( $words[ $#words ] eq "package" )
            {
                $when = "package";
            }
            else
            {
                $when = "?";
            }

            if ( $when ne "?" )
            {
                my( $module ) = $words[ $#words - 1 ];

                $who = $module;
                $section_link{ $who, $when } = $num_sections;
                $ignored{ $who, $when } = $ignore;

                if ( ! $seen{ $module } )
                {
                    $seen{ $module } = 1;
                    $module_order = "$module_order $module";
                }
            }
        }
        else
        {
            if ( hasError( $_ ) )
            {
                $num_errors++;
                chomp;
                $output = $output .
                          "</pre>\n" .
                          "<pre class='error'>\n" .
                          "\n" .
                          "<a name='error_$num_errors'>" . esc( $_ ) .
                          "</a>\n" .
                          "\n" .
                          "</pre>\n" .
                          "<pre>\n";

                $error_link{ $who, $when } .= " $num_errors";
            }
            elsif ( /warning/i )
            {
                $num_warnings++;
                chomp;
                $output = $output .
                          "</pre>\n" .
                          "<pre class='warn'>\n" .
                          "\n" .
                          "<a name='warning_$num_warnings'>" . esc( $_ ) .
                          "</a>\n" .
                          "\n" .
                          "</pre>\n" .
                          "<pre>\n";

                $warn_link{ $who, $when } .= " $num_warnings";
            }
            elsif ( /^\s*(\[echo\])?\s*# .* time: [0-9.:]+ sec/ )
            {
                my( @words ) = split( " " );
                my( $secs ) = $words[ $#words - 1 ];
                my( @t ) = split( ":", $secs );

                if ( $#t > 0 )
                {
                    $secs = $t[ 0 ] * 60 + $t[ 1 ];
                }

                $output = $output . esc( $_ );

                $elapsed_time{ $who, $when } = $secs;
            }
            else
            {
                $output = $output . esc( $_ );
            }
        }
    }

    close( BUILDLOG );
}

# ------------------------------------------------------------------
sub outputHtml
{

    print HTML "<html>\n";
    print HTML "<head>\n";

    if ( $opt_refresh > 0 )
    {
        print HTML "    <meta http-equiv='refresh' content='$opt_refresh'/>\n";
    }

    print HTML "    <style>\n";
    print HTML "        th.title { font-size: 125%; background: #ccccff; }\n";
    print HTML "        td { padding-left: 4; padding-right: 4; }\n";
    print HTML "\n";
    print HTML "        .working { background: #ffff99; }\n";
    print HTML "        .section { background: #ccccff; }\n";
    print HTML "        .success { background: #b9ffb9; }\n";
    print HTML "        .error   { background: #ff9999; }\n";
    print HTML "        .warn    { background: #ffcc99; }\n";
    print HTML "\n";
    print HTML "        .time    { text-align: right; background: #ddddff; font-family: monospace; }\n";
    print HTML "        .name    { background: #ddddff; }\n";

    print HTML "        ul.nav li { display: inline;\n";
    print HTML "                    overflow: hidden;\n";
    print HTML "                    list-style-type: none;\n";
    print HTML "                  }\n";

    print HTML "    </style>\n";
    print HTML "</head>\n";
    print HTML "<body>\n";
    print HTML "<a name='top'/>";
    print HTML "<img src=\"$logo\" alt=\"LOGO\">\n";
    print HTML "<br>";
    print HTML "<hr>";

    outputNavigation();

    outputModuleTable();
    print HTML "<p/>\n";
    outputCdTable();

    print HTML "<pre>\n";
    print HTML $output;
    print HTML "</pre>\n";

    outputNavigation();

    print HTML "<a name='bottom'/>";
    print HTML "</body>\n";

    close( HTML );
}

# ------------------------------------------------------------------
sub outputNavigation
{
    print HTML "<ul class='nav'>\n";
    print HTML "    <li>Go to:</li>";
    print HTML "    <li><a href='#top'>[top]</a></li>\n";
    print HTML "    <li><a href='#bottom'>[bottom]</a></li>\n";
    print HTML "</ul>\n";
}

# ------------------------------------------------------------------
sub outputModuleTable
{
    my( $when );
    my( $module );

    print HTML "<table>\n";

    print HTML "<thead>\n";
    print HTML "<tr>\n";
    print HTML "    <th class='title'>module</th>";
    foreach $when ( @whenlist )
    {
        print HTML "    <th/>\n";
        print HTML "    <th class='title' colspan='2'>$when</th>\n";
    }
    print HTML "</tr>\n";
    print HTML "</thead>\n";

    foreach $module ( split( " ", $module_order ) )
    {
        print HTML "<tr>\n";

        print HTML "    <td class='name'>$module</td>\n";

        foreach $when ( @whenlist )
        {
            my( $status );

            if ( $elapsed_time{ $module, $when } )
            {
                $status = "success";
            }
            else
            {
                $status = $ignored{ $module, $when } ? "n/a" : "working";
            }

            print HTML "    <td/>";
            outputStatusCell( $module, $when, $status );
            outputTimeCell( $module, $when );
        }

        print HTML "</tr>\n";
    }

    print HTML "</table>\n";
}

# ------------------------------------------------------------------
sub outputCdTable
{
    my( $when );
    my( $key );
    my( $platform );

    if ( %products )
    {
        print HTML "<table>\n";

        print HTML "<thead>\n";
        print HTML "<tr>\n";
        print HTML "    <th class='title'>product</th>";
        print HTML "    <th class='title'>platform</th>";
        print HTML "    <th/>\n";
        print HTML "    <th class='title'>status</th>\n";
        print HTML "    <th class='title'>image</th>\n";
        print HTML "    <th class='title'>iso</th>\n";
        print HTML "</tr>\n";
        print HTML "</thead>\n";

        for $key ( sort ( keys %products ) )
        {
            my( @platforms ) = split( " ", $products{ $key } );
            my( $first ) = 1;
            my( $span ) = $#platforms + 1;

            print HTML "    <tr/>\n";
            print HTML "    <tr>\n";
            print HTML "        <td class='name' rowspan='$span'>$key</td>\n";

            foreach $platform ( sort @platforms )
            {
                my( $who ) = "$key-$platform";

                if ( ! $first )
                {
                    print HTML "    <tr>\n";
                }
                print HTML "        <td class='name'>$platform</td>\n";

                print HTML "    <td/>";
                outputStatusCell( $who, "image", "success" );
                outputTimeCell( $who, "image" );

                outputTimeCell( $who, "iso" );

                print HTML "    </tr>\n";
                $first = 0;
            }
        }

        print HTML "</table>\n";
    }
}

# ------------------------------------------------------------------
sub outputStatusCell
{
    my( $who, $when, $status ) = @_;

    my( $section ) = $section_link{ $who, $when };
    my( $warn )    = $warn_link{    $who, $when };
    my( $error )   = $error_link{   $who, $when };
    my( $num );

    # Add all error links

    if ( $error )
    {
        print HTML "    <td class='error'>";
        print HTML "\n        <a href='#section_$section'>errors:</a>\n";

        foreach $num ( split( " ", $error ) )
        {
            print HTML "       &nbsp;<a href='#error_$num'>$num</a>\n";
        }

        print HTML "    ";
    }

    # Add all warning links

    if ( $warn )
    {
        if ( $error )
        {
            print HTML "<br/>\n";
        }
        else
        {
            print HTML "    <td class='warn'>";
        }

        print HTML "\n        <a href='#section_$section'>warnings:</a>\n";

        foreach $num ( split( " ", $warn ) )
        {
            print HTML "       &nbsp;<a href='#warning_$num'>$num</a>\n";
        }

        print HTML "    ";
    }

    # If no error or warnings, add section link if applicable

    if ( ! $error && ! $warn )
    {

        if ( $section )
        {
            if ( $status eq "working" )
            {
                print HTML "    <td class='working'>";
            }
            else
            {
                print HTML "    <td class='success'>";
            }
            print HTML "<a href='#section_$section'>$status</a>";
        }
        else
        {
            print HTML "    <td class='success'>";
            print HTML "        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
                               "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
        }
    }

    print HTML "</td>\n";
}

# ------------------------------------------------------------------
sub outputTimeCell
{
    my( $who, $when ) = @_;
    my( $time ) = $elapsed_time{ $who, $when };

    if ( $time )
    {
        my( $min ) = 0;
        my( $sec ) = 0;

        if ( $time >= 60 )
        {
            $min = int( $time / 60 );
            $sec = $time % 60;
        }
        else
        {
            $sec = $time;
        }

        $time = sprintf( "%d' %02d\"", $min, $sec );
    }

    if ( ! $time )
    {
        $time = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
    }

    print HTML "    <td class='time'>$time</td>\n";
}

# ------------------------------------------------------------------
sub help
{
    print STDERR "Usage: $prog -e error_file [options]\n";
    print STDERR "\n";
    print STDERR "       options:\n";
    print STDERR "           -i log_file\n";
    print STDERR "           -o html_file\n";
    print STDERR "           -e error_file\n";
    print STDERR "           -S buildserver\n";
    print STDERR "           -P port\n";
    print STDERR "           -j jobname\n";
    print STDERR "           -b path-to-build-command\n";
    print STDERR "           -r meta-refresh-in-secs\n";
    print STDERR "\n";
    print STDERR "       If neither -i nor -j is specified, stdin is used.\n";
    print STDERR "       If -o is not specified, stdout is used.\n";
}

# ------------------------------------------------------------------
if ( ! GetOptions( "infile|i=s",
                   "outfile|o=s",
                   "errorfile|e=s",
                   "refresh|r=i",
                   "port|p=s",
                   "server|s=s",
                   "binary|b=s",
                   "job|j=s",
                   "help|h" ) )
{
    help();
    exit 1;
}

if ( $opt_help )
{
    help();
    exit 0;
}

#-----------------------------------------------------------------------
if ( $opt_job )
{
    open( BUILDLOG, "$opt_binary buildlog -n $opt_job -t retail|" )
        or die "Can't open $opt_job";
}
elsif ( $opt_infile )
{
    if ( ! -f $opt_infile )
    {
        print "Error: $prog: no such file: $opt_infile\n";
        exit 1;
    }

    open( BUILDLOG, "<$opt_infile" )
        or die "Can't open $opt_infile";
}
else
{
    open( BUILDLOG, "<&=STDIN" );
}
 
#-----------------------------------------------------------------------
if ( ! $opt_errorfile )
{
    print "Error: $prog: '-e error_file' is required\n";
    help();
    exit 1;
}
elsif ( ! -f $opt_errorfile )
{
    print "Error: $prog: no such file: $opt_errorfile\n";
    exit 1;
}
else
{
    loadErrorList( $opt_errorfile );
}

#-----------------------------------------------------------------------
if ( $opt_outfile )
{
    open( HTML, ">$opt_outfile" )
        or die "Can't open $opt_outfile";
}
else
{
    open( HTML, ">&=STDOUT" );
    $| = 1;
}

$ENV{ BLDSERVER } = "$opt_server:$opt_port";

processLog();
outputHtml();
